=== Initialization ===

#DEFINE COMMAND, OLE
#DEFINE FUNCTION, OLE
EXTERNAL VDSOLEAUT.DLL[,ignored]

=== A comment about OLE and COIN variants ===

OLE and COIN (JSON, YAML) variants exist in different spaces.
Their numbers are completetly independent. COIN (JSON, YAML)
variants are structured entities heavily tied to JSON and
YAML. OLE variants are nothing but either plain values or
object references. Different OLE types are there to only make
calls to OLE objects.

=== OLE Variant API ===

Deallocation:
OLE CLOSE, variant, %V

Deallocates variant #%V . Has no effect if already deallocated.

OLE CLOSE, variant, ALL

Deallocates all variants if any.


Type of:
%D = @OLE(variant, %V, TYPEOF)

Returns type of variant #%V. Known types are: 
STRING, INTEGER, BOOLEAN, REAL, OBJECT, NULL, UNDEFINED, DATETIME, UNKNOWN.


Serialization:
%D = @OLE(variant, %V, TOSTRING)
%D = @OLE(variant, %V, TOUTF)

Returns string representation of variant #%V. ANSI or UTF-8.


Create variant:
%V = @OLE(variant, 23, {STRING/UTF/INTEGER/BOOLEAN/REAL})
%V = @OLE(variant, class name, {OBJECT/THREADEDOBJECT})

Allocates new variant with specific value of specific type.
"SAPI.SpVoice, OBJECT" would create new OLE object of registered
SAPI.SpVoice class name. THREADEDOBJECT acts like OBJECT, but
creates object in the MTA. All calls work slower, but
asynchronous interaction is possible.


Enumerate:
%E = @OLE(variant, %V, ENUMERATE)

Creates new enumerator #%E (should be deallocated).


Query object property:
%Y = @OLE(variant, %V, GET, variant, name[, value, [Name:]type...])

Get property name[index, index, index, ...] where each index is
described by pair (value, [Name:]type).

%Y is new variant id (should be deallocated).

%Y = @OLE(variant, %V, GET, {string/utf}, name[, value, [Name:]type...])

%Y is string representation in either ANSI or UTF-8.

%P = @OLE(variant, %V, GET, promise, name[, value, [Name:]type...])

%P is new promise id (should be deallocated). Callee, method name and arguments 
are bound in the main thread, then execution continues in background thread.
When execution finishes, @EVENT() OLEASYNCEVENT is generated with @MSGPARAMS(W)
set to be promise id. Object in variant #%V must have been created in the MTA
(THREADEDOBJECT). Otherwise COM forces synchronous execution in the main thread.

%E = @OLE(variant, %V, GET, enumerator, name[, value, [Name:]type...])

#%E is new enumerator (should be deallocated).


Set object property:
OLE MODIFY, variant, %V, PUT, name[, value, [Name:]type...], value, type

Set property name[index, index, index, ...] to be item where
each index and item are described by pair "value, type".

%P = @OLE(variant, %V, PUT, promise, name[, value, [Name:]type...])

%P is new promise id (should be deallocated). Callee, method name and arguments 
are bound in the main thread, then execution continues in background thread.
When execution finishes, @EVENT() OLEASYNCEVENT is generated with @MSGPARAMS(W)
set to be promise id. Object in variant #%V must have been created in the MTA
(THREADEDOBJECT). Otherwise COM forces synchronous execution in the main thread.


Invoke object method:
%Y = @OLE(variant, %V, DO, variant, name[, value, [Name:]type...])
OLE INVOKE, variant, %V, DO, name[, value, [Name:]type...]

Invokes method name(argument, argument, ...) where each argument is
described by "value, type" pair. "Name:type" syntax can be used for
named parameters (makes sense for e.g. MS Word Search with tons of
parametes). Named parameters must follow positional parameters.

%Y is new variant id (should be deallocated).

%Y = @OLE(variant, %V, DO, {string/utf}, name[, value, [Name:]type...])

%Y is string representation (ANSI or UTF-8).

%P = @OLE(variant, %V, DO, promise, name[, value, [Name:]type...])

%P is new promise id (should be deallocated). Callee, method name and arguments 
are bound in the main thread, then execution continues in background thread.
When execution finishes, @EVENT() OLEASYNCEVENT is generated with @MSGPARAMS(W)
set to be promise id. Object in variant #%V must have been created in the MTA
(THREADEDOBJECT). Otherwise COM forces synchronous execution in the main thread.

%E = @OLE(variant, %V, DO, enumerator, name[, value, [Name:]type...])

#%E is new enumerator (should be deallocated).


=== OLE Enumerator API ===

Deallocation:
OLE CLOSE, enumerator, %E

Deallocates enumerator #%E . Has no effect if already deallocated.

OLE CLOSE, enumerator, ALL

Deallocates all enumerators if any.


Query next item:
%Y = @OLE(enumerator, %E, NEXT)

%Y is new variant id (should be deallocated).


=== Promises ===

Deallocation:
OLE CLOSE, promise, %P

Deallocates promise #%P . Execution does not abort, but there will be no notification.

OLE CLOSE, promise, ALL

Deallocates all promises. Execution does not abort, but there will be no notification.


Completion test:
%B = @OLE(promise, %P, ISCOMPLETED)

Checks if promise #%P is completed. Returns either "TRUE" or empty string.

%B = @OLE(promise, %P, ISRUNNING)

Checks if promise #%P is still running. Returns either "TRUE" or empty string.


Value query:
%Y = @OLE(promise, %P, VALUE)

%Y is new variant id (should be deallocated).

%Y = @OLE(promise, %P, {TOSTRING/TOUTF})

%Y is string representation in either ANSI or UTF-8.



=== Examples ===

%S = @OLE(variant, SAPI.SpVoice, THREADEDOBJECT)

INFO SAPI.SpVoice created with type @OLE(variant, %S, TYPEOF)
# OBJECT if Speech API is installed

OLE INVOKE, variant, %S, DO, Speak, This is a test, STRING
# Speak text synchronously with default voice

%P = @OLE(variant, %S, DO, promise, Speak, This is another test. You should hear speech and see message at the same time, STRING)
# Speak text asynchronously with default voice

INFO Asynchronous method invocation
# One should see this message in parallel with actual voice

WHILE @OLE(promise, %P, ISRUNNING)
  # WAIT EVENT
  # Only valid when dialog is displayed
  
  WAIT 1
  # UI is responding while background thread executes
WEND

OLE CLOSE, promise, %P

INFO Volume property by default: @OLE(variant, %S, GET, string, Volume)
# 100 by default

OLE MODIFY, variant, %S, PUT, Volume, 80, INTEGER
# Set volume to 80

INFO Volume property after synchronous modification: @OLE(variant, %S, GET, string, Volume)
# Now it is 80

%P = @OLE(variant, %S, PUT, promise, Volume, 100, INTEGER)
# Asynchronous property modification

INFO Volume property after asynchronous modification: @OLE(variant, %S, GET, string, Volume)
# Can be still 80 if background thread starts slow (didn't manage to get this)
# Changes to 100 if THREADEDOBJECT changed to OBJECT in the beginning

WHILE @OLE(promise, %P, ISRUNNING)
  WAIT 1
  # UI is responding while background thread executes
WEND

INFO Volume property after waiting for completion: @OLE(variant, %S, GET, string, Volume)

OLE CLOSE, promise, %P

%V = @OLE(variant, %S, DO, variant, GetVoices)
%E = @OLE(variant, %V, ENUMERATE)
OLE CLOSE, variant, %V

%V = @OLE(enumerator, %E, NEXT)
WHILE %V
  INFO Voice detected: @OLE(variant, %V, DO, string, GetDescription)
  
  OLE CLOSE, variant, %V
  %V = @OLE(enumerator, %E, NEXT)
WEND

OLE CLOSE, enumerator, %E

OLE CLOSE, variant, %S
